The following content is based on the tidymodels documentation and Alisson Hill’s tidymodels workshop.

In this tutorial, we’ll explore a tidymodels package, recipes, which is designed to help you preprocess your data before training your model.

Recipes are built as a series of preprocessing steps, such as:

  • converting qualitative predictors to indicator variables (also known as dummy variables),
  • transforming data to be on a different scale (e.g., taking the logarithm of a variable),
  • transforming whole groups of predictors together,
  • extracting key features from raw variables (e.g., getting the day of the week out of a date variable),

and so on. If you are familiar with R’s formula interface, a lot of this might sound familiar and like what a formula already does. Recipes can be used to do many of the same things, but they have a much wider range of possibilities. This article shows how to use recipes for modeling.

In summary, the idea of the recipes package is to define a recipe or blueprint that can be used to sequentially define the encodings and preprocessing of the data (i.e. “feature engineering”) before we build our models.

1 Data preparation

Import data and split the data into training and testing sets using initial_split()

library(tidyverse)
library(tidymodels)

ames <- read_csv("https://raw.githubusercontent.com/kirenz/datasets/master/ames.csv")

ames <- ames %>%
 select(-matches("Qu"))

set.seed(100)

new_split <- initial_split(ames) 
new_train <- training(new_split) 
new_test <- testing(new_split)

2 Recipe

Next, we use a recipe() to build a set of steps for data preprocessing and feature engineering.

  • First, we must tell the recipe() what our model is going to be (using a formula here) and what our training data is.
  • step_novel() will convert all nominal variables to factors.
  • We then convert the factor columns into (one or more) numeric binary (0 and 1) variables for the levels of the training data.
  • We remove any numeric variables that have zero variance.
  • We normalize (center and scale) the numeric variables.

2.1 recipe()

ames_rec <-
  recipe(Sale_Price ~ ., data = new_train) %>%
  step_novel(all_nominal(), -all_outcomes()) %>%
  step_dummy(all_nominal()) %>%
  step_zv(all_predictors()) %>%
  step_normalize(all_predictors())

# Show the content of our recipe
ames_rec
## Data Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor         73
## 
## Operations:
## 
## Novel factor level assignment for all_nominal(), -all_outcomes()
## Dummy variables from all_nominal()
## Zero variance filter on all_predictors()
## Centering and scaling for all_predictors()

2.2 prep()

Finally, we prep() the recipe(). This means we actually do something with the steps and our training data.

ames_prep <- prep(ames_rec)

Print a summary of our prepped recipe:

summary(ames_prep)

2.3 juice()

To obtain the Dataframe from the prepped recipe, we use the function juice(). When we juice() the recipe, we squeeze that training data back out, transformed in the ways we specified:

juice(ames_prep)

The prepped recipe ames_prep contains all our transformations for data preprocessing and feature engineering, as well as the data these transformations were estimated from.

2.4 bake()

We now can simply apply all of the recipe transformations to the testing data. The function to perform this is called bake():

test_trans <- bake(ames_prep, new_data = new_test)

Now it’s time to specify and then fit our models.

3 Model building

3.1 Specify model

lm_spec <- 
  linear_reg() %>% 
  set_engine("lm") %>% 
  set_mode(mode = "regression")

3.2 Fit model

lm_fit <- 
  lm_spec %>%
  fit(Sale_Price ~ . , data = juice(ames_prep))

3.3 Evaluate model

set.seed(100)

cv_folds <-
 vfold_cv(juice(ames_prep), 
          v = 10, 
          strata = Sale_Price,
          breaks = 4) 

lm_res <-
  lm_spec %>% 
  fit_resamples(
    Sale_Price ~ .,
    resamples = cv_folds
    )

lm_res %>% 
  collect_metrics()

3.4 Evaluate final model

Finally, let’s use our testing data and see how we can expect this model to perform on new data.

lm_fit %>% 
 predict(test_trans) %>%
 mutate(truth = test_trans$Sale_Price) %>%
 rmse(truth, .pred)

4 Components of our recipe

Let’s have a closer look at the different components of the recipe.

4.1 recipe()

First of all, we created a simple recipe (we call it rec) containing only an outcome (Sale_Price) and predictors (all other variables in the dataset: .). To demonstrate the use of recipes step by step, we create a new object with the name rec:

rec <- recipe(Sale_Price ~ ., data = ames)

The formula Sale_Price ~ . indicates outcomes vs predictors.

4.2 Helper functions

Here some helper functions for selecting sets of variables:

  • all_predictors(): Each x variable (right side of ~)
  • all_outcomes(): Each y variable (left side of ~)
  • all_numeric(): Each numeric variable
  • all_nominal(): Each categorical variable (e.g. factor, string)
  • dplyr::select() helpers starts_with(‘Lot_’), etc.

4.3 step_novel()

step_novel() will convert all nominal variables to factors. It adds a catch-all level to a factor for any new values, which lets R intelligently predict new levels in the test set. Missing values will remain missing.

rec %>%
  step_novel(all_nominal(), -all_outcomes())
## Data Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor         73
## 
## Operations:
## 
## Novel factor level assignment for all_nominal(), -all_outcomes()

4.4 step_dummy()

Converts nominal data into dummy variables.

rec %>%
 step_dummy(all_nominal())
## Data Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor         73
## 
## Operations:
## 
## Dummy variables from all_nominal()

4.5 step_zv()

step_zv() removes zero variance variables (variables that contain only a single value).

rec %>%
  step_zv(all_predictors())
## Data Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor         73
## 
## Operations:
## 
## Zero variance filter on all_predictors()

When the recipe is applied to the data set, a column could contain only zeros. This is a “zero-variance predictor” that has no information within the column. While some R functions will not produce an error for such predictors, it usually causes warnings and other issues. step_zv() will remove columns from the data when the training set data have a single value- This step should be added to the recipe after step_dummy().

4.6 step_normalize()

Centers then scales numeric variable (mean = 0, sd = 1)

rec %>%
  step_normalize(all_numeric())
## Data Recipe
## 
## Inputs:
## 
##       role #variables
##    outcome          1
##  predictor         73
## 
## Operations:
## 
## Centering and scaling for all_numeric()

5 Workflows

To combine the data preparation with the model building, we could use the package workflows. A workflow is an object that can bundle together your pre-processing, modeling, and post-processing requests:

5.1 workflow()

new_wf <-
 workflow() %>%
 add_recipe(ames_rec) %>%
 add_model(lm_spec)

5.2 fit()

new_wf_fit <- 
  new_wf %>% 
  fit(data = new_train)

5.3 predict() & rmse()

Make predictions and calculate the RMSE for our training data.

new_wf_fit %>% 
 predict(new_train) %>%
 mutate(truth = new_train$Sale_Price) %>%
 rmse(truth, .pred)
LS0tCnRpdGxlOiAiVGlkeW1vZGVscyBJSUk6IEJ1aWxkIE1vZGVscyB3aXRoIFJlY2lwZXMiCnN1YnRpdGxlOiAiTGVhcm4gaG93IHRvIGJ1aWxkIG1vZGVscyB3aXRoIHRpZHltb2RlbHMsIFBhcnQgMyIKYXV0aG9yOiAiUHJvZi4gRHIuIEphbiBLaXJlbnoiCm91dHB1dDoKIGh0bWxfZG9jdW1lbnQ6CiAgY29kZV9kb3dubG9hZDogdHJ1ZSAKICBjc3M6IHN0eWxlLmNzcyAKICBmaWdfaGVpZ2h0OiA2CiAgZmlnX3dpZHRoOiA4CiAgaGlnaGxpZ2h0OiB0YW5nbwogIG51bWJlcl9zZWN0aW9uczogeWVzCiAgdGhlbWU6IHBhcGVyCiAgdG9jOiB5ZXMKICB0b2NfZGVwdGg6IDMKICB0b2NfZmxvYXQ6IAogICAgY29sbGFwc2VkOiBmYWxzZQogICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAKICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAoJZWNobyA9IFRSVUUsCgltZXNzYWdlID0gRkFMU0UsCgl3YXJuaW5nID0gRkFMU0UKKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5bW9kZWxzKQpgYGAKCipUaGUgZm9sbG93aW5nIGNvbnRlbnQgaXMgYmFzZWQgb24gdGhlIFt0aWR5bW9kZWxzIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LnRpZHltb2RlbHMub3JnL3N0YXJ0L3JlY2lwZXMvKSBhbmQgQWxpc3NvbiBIaWxsJ3MgW3RpZHltb2RlbHMgd29ya3Nob3BdKGh0dHBzOi8vYWxpc29uLnJiaW5kLmlvL3RhZ3MvdGlkeW1vZGVscy8pLioKCkluIHRoaXMgdHV0b3JpYWwsIHdl4oCZbGwgZXhwbG9yZSBhIHRpZHltb2RlbHMgcGFja2FnZSwgYHJlY2lwZXNgLCB3aGljaCBpcyBkZXNpZ25lZCB0byBoZWxwIHlvdSBwcmVwcm9jZXNzIHlvdXIgZGF0YSBiZWZvcmUgdHJhaW5pbmcgeW91ciBtb2RlbC4gCgpSZWNpcGVzIGFyZSBidWlsdCBhcyBhIHNlcmllcyBvZiBwcmVwcm9jZXNzaW5nIHN0ZXBzLCBzdWNoIGFzOgoKKiBjb252ZXJ0aW5nIHF1YWxpdGF0aXZlIHByZWRpY3RvcnMgdG8gaW5kaWNhdG9yIHZhcmlhYmxlcyAoYWxzbyBrbm93biBhcyBkdW1teSB2YXJpYWJsZXMpLAoqIHRyYW5zZm9ybWluZyBkYXRhIHRvIGJlIG9uIGEgZGlmZmVyZW50IHNjYWxlIChlLmcuLCB0YWtpbmcgdGhlIGxvZ2FyaXRobSBvZiBhIHZhcmlhYmxlKSwKKiB0cmFuc2Zvcm1pbmcgd2hvbGUgZ3JvdXBzIG9mIHByZWRpY3RvcnMgdG9nZXRoZXIsCiogZXh0cmFjdGluZyBrZXkgZmVhdHVyZXMgZnJvbSByYXcgdmFyaWFibGVzIChlLmcuLCBnZXR0aW5nIHRoZSBkYXkgb2YgdGhlIHdlZWsgb3V0IG9mIGEgZGF0ZSB2YXJpYWJsZSksCgphbmQgc28gb24uIElmIHlvdSBhcmUgZmFtaWxpYXIgd2l0aCBS4oCZcyBmb3JtdWxhIGludGVyZmFjZSwgYSBsb3Qgb2YgdGhpcyBtaWdodCBzb3VuZCBmYW1pbGlhciBhbmQgbGlrZSB3aGF0IGEgZm9ybXVsYSBhbHJlYWR5IGRvZXMuIFJlY2lwZXMgY2FuIGJlIHVzZWQgdG8gZG8gbWFueSBvZiB0aGUgc2FtZSB0aGluZ3MsIGJ1dCB0aGV5IGhhdmUgYSBtdWNoIHdpZGVyIHJhbmdlIG9mIHBvc3NpYmlsaXRpZXMuIFRoaXMgYXJ0aWNsZSBzaG93cyBob3cgdG8gdXNlIHJlY2lwZXMgZm9yIG1vZGVsaW5nLgoKSW4gc3VtbWFyeSwgdGhlIGlkZWEgb2YgdGhlIFtyZWNpcGVzIHBhY2thZ2VdKGh0dHBzOi8vcmVjaXBlcy50aWR5bW9kZWxzLm9yZykgaXMgdG8gZGVmaW5lIGEgcmVjaXBlIG9yIGJsdWVwcmludCB0aGF0IGNhbiBiZSB1c2VkIHRvIHNlcXVlbnRpYWxseSBkZWZpbmUgdGhlIGVuY29kaW5ncyBhbmQgcHJlcHJvY2Vzc2luZyBvZiB0aGUgZGF0YSAoaS5lLiDigJxmZWF0dXJlIGVuZ2luZWVyaW5n4oCdKSBiZWZvcmUgd2UgYnVpbGQgb3VyIG1vZGVscy4KCiMgRGF0YSBwcmVwYXJhdGlvbgoKSW1wb3J0IGRhdGEgYW5kIHNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyB1c2luZyBgaW5pdGlhbF9zcGxpdCgpYAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRpZHltb2RlbHMpCgphbWVzIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20va2lyZW56L2RhdGFzZXRzL21hc3Rlci9hbWVzLmNzdiIpCgphbWVzIDwtIGFtZXMgJT4lCiBzZWxlY3QoLW1hdGNoZXMoIlF1IikpCgpzZXQuc2VlZCgxMDApCgpuZXdfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChhbWVzKSAKbmV3X3RyYWluIDwtIHRyYWluaW5nKG5ld19zcGxpdCkgCm5ld190ZXN0IDwtIHRlc3RpbmcobmV3X3NwbGl0KQoKYGBgCgojIFJlY2lwZQoKTmV4dCwgd2UgdXNlIGEgYHJlY2lwZSgpYCB0byBidWlsZCBhIHNldCBvZiBzdGVwcyBmb3IgZGF0YSBwcmVwcm9jZXNzaW5nIGFuZCBmZWF0dXJlIGVuZ2luZWVyaW5nLgoKKiBGaXJzdCwgd2UgbXVzdCB0ZWxsIHRoZSBgcmVjaXBlKClgIHdoYXQgb3VyIG1vZGVsIGlzIGdvaW5nIHRvIGJlICh1c2luZyBhIGZvcm11bGEgaGVyZSkgYW5kIHdoYXQgb3VyIHRyYWluaW5nIGRhdGEgaXMuCiogYHN0ZXBfbm92ZWwoKWAgd2lsbCBjb252ZXJ0IGFsbCBub21pbmFsIHZhcmlhYmxlcyB0byBmYWN0b3JzLgoqIFdlIHRoZW4gY29udmVydCB0aGUgZmFjdG9yIGNvbHVtbnMgaW50byAob25lIG9yIG1vcmUpIG51bWVyaWMgYmluYXJ5ICgwIGFuZCAxKSB2YXJpYWJsZXMgZm9yIHRoZSBsZXZlbHMgb2YgdGhlIHRyYWluaW5nIGRhdGEuCiogV2UgcmVtb3ZlIGFueSBudW1lcmljIHZhcmlhYmxlcyB0aGF0IGhhdmUgemVybyB2YXJpYW5jZS4KKiBXZSBub3JtYWxpemUgKGNlbnRlciBhbmQgc2NhbGUpIHRoZSBudW1lcmljIHZhcmlhYmxlcy4gCgojIyByZWNpcGUoKQoKYGBge3J9CgphbWVzX3JlYyA8LQogIHJlY2lwZShTYWxlX1ByaWNlIH4gLiwgZGF0YSA9IG5ld190cmFpbikgJT4lCiAgc3RlcF9ub3ZlbChhbGxfbm9taW5hbCgpLCAtYWxsX291dGNvbWVzKCkpICU+JQogIHN0ZXBfZHVtbXkoYWxsX25vbWluYWwoKSkgJT4lCiAgc3RlcF96dihhbGxfcHJlZGljdG9ycygpKSAlPiUKICBzdGVwX25vcm1hbGl6ZShhbGxfcHJlZGljdG9ycygpKQoKIyBTaG93IHRoZSBjb250ZW50IG9mIG91ciByZWNpcGUKYW1lc19yZWMKICAKYGBgCgojIyBwcmVwKCkKCkZpbmFsbHksIHdlIGBwcmVwKClgIHRoZSBgcmVjaXBlKClgLiBUaGlzIG1lYW5zIHdlIGFjdHVhbGx5IGRvIHNvbWV0aGluZyB3aXRoIHRoZSBzdGVwcyBhbmQgb3VyIHRyYWluaW5nIGRhdGEuCgpgYGB7cn0KCmFtZXNfcHJlcCA8LSBwcmVwKGFtZXNfcmVjKQoKYGBgCgoKUHJpbnQgYSBzdW1tYXJ5IG9mIG91ciBwcmVwcGVkIHJlY2lwZToKCmBgYHtyfQoKc3VtbWFyeShhbWVzX3ByZXApCgpgYGAKCiMjIGp1aWNlKCkKClRvIG9idGFpbiB0aGUgRGF0YWZyYW1lIGZyb20gdGhlIHByZXBwZWQgcmVjaXBlLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBqdWljZSgpYC4gV2hlbiB3ZSBganVpY2UoKWAgdGhlIHJlY2lwZSwgd2Ugc3F1ZWV6ZSB0aGF0IHRyYWluaW5nIGRhdGEgYmFjayBvdXQsIHRyYW5zZm9ybWVkIGluIHRoZSB3YXlzIHdlIHNwZWNpZmllZDoKCmBgYHtyfQoKanVpY2UoYW1lc19wcmVwKQoKYGBgCgpUaGUgcHJlcHBlZCByZWNpcGUgYGFtZXNfcHJlcGAgY29udGFpbnMgYWxsIG91ciB0cmFuc2Zvcm1hdGlvbnMgZm9yIGRhdGEgcHJlcHJvY2Vzc2luZyBhbmQgZmVhdHVyZSBlbmdpbmVlcmluZywgKmFzIHdlbGwgYXMqIHRoZSBkYXRhIHRoZXNlIHRyYW5zZm9ybWF0aW9ucyB3ZXJlIGVzdGltYXRlZCBmcm9tLiAKCiMjIGJha2UoKQoKV2Ugbm93IGNhbiBzaW1wbHkgYXBwbHkgYWxsIG9mIHRoZSByZWNpcGUgdHJhbnNmb3JtYXRpb25zIHRvIHRoZSB0ZXN0aW5nIGRhdGEuIFRoZSBmdW5jdGlvbiB0byBwZXJmb3JtIHRoaXMgaXMgY2FsbGVkIGBiYWtlKClgOgoKCmBgYHtyfQoKdGVzdF90cmFucyA8LSBiYWtlKGFtZXNfcHJlcCwgbmV3X2RhdGEgPSBuZXdfdGVzdCkKCmBgYAoKCk5vdyBpdCdzIHRpbWUgdG8gKipzcGVjaWZ5KiogYW5kIHRoZW4gKipmaXQqKiBvdXIgbW9kZWxzLiAKCiMgTW9kZWwgYnVpbGRpbmcKCiMjIFNwZWNpZnkgbW9kZWwKCmBgYHtyfQoKbG1fc3BlYyA8LSAKICBsaW5lYXJfcmVnKCkgJT4lIAogIHNldF9lbmdpbmUoImxtIikgJT4lIAogIHNldF9tb2RlKG1vZGUgPSAicmVncmVzc2lvbiIpCgpgYGAKCiMjIEZpdCBtb2RlbAoKYGBge3J9CgpsbV9maXQgPC0gCiAgbG1fc3BlYyAlPiUKICBmaXQoU2FsZV9QcmljZSB+IC4gLCBkYXRhID0ganVpY2UoYW1lc19wcmVwKSkKCmBgYAoKCiMjIEV2YWx1YXRlIG1vZGVsCgpgYGB7cn0KCnNldC5zZWVkKDEwMCkKCmN2X2ZvbGRzIDwtCiB2Zm9sZF9jdihqdWljZShhbWVzX3ByZXApLCAKICAgICAgICAgIHYgPSAxMCwgCiAgICAgICAgICBzdHJhdGEgPSBTYWxlX1ByaWNlLAogICAgICAgICAgYnJlYWtzID0gNCkgCgpsbV9yZXMgPC0KICBsbV9zcGVjICU+JSAKICBmaXRfcmVzYW1wbGVzKAogICAgU2FsZV9QcmljZSB+IC4sCiAgICByZXNhbXBsZXMgPSBjdl9mb2xkcwogICAgKQoKbG1fcmVzICU+JSAKICBjb2xsZWN0X21ldHJpY3MoKQoKYGBgCgoKIyMgRXZhbHVhdGUgZmluYWwgbW9kZWwKCkZpbmFsbHksIGxldCdzIHVzZSBvdXIgdGVzdGluZyBkYXRhIGFuZCBzZWUgaG93IHdlIGNhbiBleHBlY3QgdGhpcyBtb2RlbCB0byBwZXJmb3JtIG9uIG5ldyBkYXRhLgoKYGBge3J9CgpsbV9maXQgJT4lIAogcHJlZGljdCh0ZXN0X3RyYW5zKSAlPiUKIG11dGF0ZSh0cnV0aCA9IHRlc3RfdHJhbnMkU2FsZV9QcmljZSkgJT4lCiBybXNlKHRydXRoLCAucHJlZCkKCmBgYAoKCiMgQ29tcG9uZW50cyBvZiBvdXIgcmVjaXBlCgpMZXQncyBoYXZlIGEgY2xvc2VyIGxvb2sgYXQgdGhlIGRpZmZlcmVudCBjb21wb25lbnRzIG9mIHRoZSByZWNpcGUuCgojIyByZWNpcGUoKQoKRmlyc3Qgb2YgYWxsLCB3ZSBjcmVhdGVkIGEgc2ltcGxlIHJlY2lwZSAod2UgY2FsbCBpdCBgcmVjYCkgY29udGFpbmluZyBvbmx5IGFuIG91dGNvbWUgKGBTYWxlX1ByaWNlYCkgYW5kIHByZWRpY3RvcnMgKGFsbCBvdGhlciB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQ6IGAuYCkuIFRvIGRlbW9uc3RyYXRlIHRoZSB1c2Ugb2YgcmVjaXBlcyBzdGVwIGJ5IHN0ZXAsIHdlIGNyZWF0ZSBhIG5ldyBvYmplY3Qgd2l0aCB0aGUgbmFtZSBgcmVjYDoKCmBgYHtyfQoKcmVjIDwtIHJlY2lwZShTYWxlX1ByaWNlIH4gLiwgZGF0YSA9IGFtZXMpCgpgYGAKClRoZSBmb3JtdWxhIGBTYWxlX1ByaWNlIH4gLmAgaW5kaWNhdGVzIG91dGNvbWVzIHZzIHByZWRpY3RvcnMuCgojIyBIZWxwZXIgZnVuY3Rpb25zCgpIZXJlIHNvbWUgaGVscGVyIGZ1bmN0aW9ucyBmb3Igc2VsZWN0aW5nIHNldHMgb2YgdmFyaWFibGVzOgoKKiBgYWxsX3ByZWRpY3RvcnMoKWA6IEVhY2ggeCB2YXJpYWJsZSAocmlnaHQgc2lkZSBvZiB+KQoqIGBhbGxfb3V0Y29tZXMoKWA6IEVhY2ggeSB2YXJpYWJsZSAobGVmdCBzaWRlIG9mIH4pCiogYGFsbF9udW1lcmljKClgOiBFYWNoIG51bWVyaWMgdmFyaWFibGUKKiBgYWxsX25vbWluYWwoKWA6IEVhY2ggY2F0ZWdvcmljYWwgdmFyaWFibGUgKGUuZy4gZmFjdG9yLCBzdHJpbmcpCiogYGRwbHlyOjpzZWxlY3QoKWAgaGVscGVycyBzdGFydHNfd2l0aCgnTG90XycpLCBldGMuCgoKIyMgc3RlcF9ub3ZlbCgpCgpbYHN0ZXBfbm92ZWwoKWBdKGh0dHBzOi8vcmVjaXBlcy50aWR5bW9kZWxzLm9yZy9yZWZlcmVuY2Uvc3RlcF9ub3ZlbC5odG1sKSB3aWxsIGNvbnZlcnQgYWxsIG5vbWluYWwgdmFyaWFibGVzIHRvIGZhY3RvcnMuIEl0IGFkZHMgYSBjYXRjaC1hbGwgbGV2ZWwgdG8gYSBmYWN0b3IgZm9yIGFueSBuZXcgdmFsdWVzLCB3aGljaCBsZXRzIFIgaW50ZWxsaWdlbnRseSBwcmVkaWN0IG5ldyBsZXZlbHMgaW4gdGhlIHRlc3Qgc2V0LiBNaXNzaW5nIHZhbHVlcyB3aWxsIHJlbWFpbiBtaXNzaW5nLgoKYGBge3J9CgpyZWMgJT4lCiAgc3RlcF9ub3ZlbChhbGxfbm9taW5hbCgpLCAtYWxsX291dGNvbWVzKCkpCgpgYGAKCiMjIHN0ZXBfZHVtbXkoKQoKQ29udmVydHMgbm9taW5hbCBkYXRhIGludG8gZHVtbXkgdmFyaWFibGVzLgoKYGBge3J9CgpyZWMgJT4lCiBzdGVwX2R1bW15KGFsbF9ub21pbmFsKCkpCgpgYGAKCgojIyBzdGVwX3p2KCkKCmBzdGVwX3p2KClgIHJlbW92ZXMgemVybyB2YXJpYW5jZSB2YXJpYWJsZXMgKHZhcmlhYmxlcyB0aGF0IGNvbnRhaW4gb25seSBhIHNpbmdsZSB2YWx1ZSkuIAoKYGBge3J9CgpyZWMgJT4lCiAgc3RlcF96dihhbGxfcHJlZGljdG9ycygpKQoKYGBgCgpXaGVuIHRoZSByZWNpcGUgaXMgYXBwbGllZCB0byB0aGUgZGF0YSBzZXQsIGEgY29sdW1uIGNvdWxkIGNvbnRhaW4gb25seSB6ZXJvcy4gVGhpcyBpcyBhICIqemVyby12YXJpYW5jZSBwcmVkaWN0b3IqIiB0aGF0IGhhcyBubyBpbmZvcm1hdGlvbiB3aXRoaW4gdGhlIGNvbHVtbi4gV2hpbGUgc29tZSBSIGZ1bmN0aW9ucyB3aWxsIG5vdCBwcm9kdWNlIGFuIGVycm9yIGZvciBzdWNoIHByZWRpY3RvcnMsIGl0IHVzdWFsbHkgY2F1c2VzIHdhcm5pbmdzIGFuZCBvdGhlciBpc3N1ZXMuIGBzdGVwX3p2KClgIHdpbGwgcmVtb3ZlIGNvbHVtbnMgZnJvbSB0aGUgZGF0YSB3aGVuIHRoZSB0cmFpbmluZyBzZXQgZGF0YSBoYXZlIGEgc2luZ2xlIHZhbHVlLSBUaGlzIHN0ZXAgc2hvdWxkIGJlIGFkZGVkIHRvIHRoZSByZWNpcGUgYWZ0ZXIgYHN0ZXBfZHVtbXkoKWAuCgojIyBzdGVwX25vcm1hbGl6ZSgpCgpDZW50ZXJzIHRoZW4gc2NhbGVzIG51bWVyaWMgdmFyaWFibGUgKG1lYW4gPSAwLCBzZCA9IDEpCgpgYGB7cn0KCnJlYyAlPiUKICBzdGVwX25vcm1hbGl6ZShhbGxfbnVtZXJpYygpKQoKYGBgCgoKIyBXb3JrZmxvd3MKClRvIGNvbWJpbmUgdGhlIGRhdGEgcHJlcGFyYXRpb24gd2l0aCB0aGUgbW9kZWwgYnVpbGRpbmcsIHdlIGNvdWxkIHVzZSB0aGUgcGFja2FnZSBbd29ya2Zsb3dzXShodHRwczovL3dvcmtmbG93cy50aWR5bW9kZWxzLm9yZykuIEEgd29ya2Zsb3cgaXMgYW4gb2JqZWN0IHRoYXQgY2FuIGJ1bmRsZSB0b2dldGhlciB5b3VyIHByZS1wcm9jZXNzaW5nLCBtb2RlbGluZywgYW5kIHBvc3QtcHJvY2Vzc2luZyByZXF1ZXN0czoKCiMjIHdvcmtmbG93KCkKCmBgYHtyfQoKbmV3X3dmIDwtCiB3b3JrZmxvdygpICU+JQogYWRkX3JlY2lwZShhbWVzX3JlYykgJT4lCiBhZGRfbW9kZWwobG1fc3BlYykKCmBgYAoKIyMgZml0KCkKCgpgYGB7cn0KCm5ld193Zl9maXQgPC0gCiAgbmV3X3dmICU+JSAKICBmaXQoZGF0YSA9IG5ld190cmFpbikKCmBgYAoKIyMgcHJlZGljdCgpICYgcm1zZSgpCgpNYWtlIHByZWRpY3Rpb25zIGFuZCBjYWxjdWxhdGUgdGhlIFJNU0UgZm9yIG91ciB0cmFpbmluZyBkYXRhLiAKCgpgYGB7cn0KbmV3X3dmX2ZpdCAlPiUgCiBwcmVkaWN0KG5ld190cmFpbikgJT4lCiBtdXRhdGUodHJ1dGggPSBuZXdfdHJhaW4kU2FsZV9QcmljZSkgJT4lCiBybXNlKHRydXRoLCAucHJlZCkKCmBgYAoKCg==